//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


package ants.p2p;

import java.net.*;
import java.io.*;
import javax.net.*;
import javax.net.ssl.*;
import com.jcraft.jzlib.*;
import org.apache.log4j.*;
import ants.p2p.utils.net.*;
import ants.p2p.utils.addresses.*;
import ants.p2p.query.QueryMessage;

public class DirectNeighbour implements Neighbour{
  InetAddress ip;
  int port;
  Ant local;
  Socket s;
  static long queuedMessages = 0;

  private static Logger _logger = Logger.getLogger(DirectNeighbour.class.getName());

  public DirectNeighbour(String ip, Ant local) throws Exception{
    this.local = local;
    if(ip.startsWith("/"))
      ip = ip.substring(1);
    String[] ipPort = ip.split("[:]");
    this.ip = InetAddress.getByName(ipPort[0]);
    this.port = Integer.parseInt(ipPort[1]);
    if(!InetAddressWatchdog.getInstance().allowedAddress(this.ip.getHostAddress()))
      throw new Exception("Address not allowed! "+this.ip);
    if(this.ip.getHostAddress().equals(local.getLocalInetAddress())) throw new Exception("Loopback connection!");
    this.s = ants.p2p.security.sockets.SSLProvider.getSSLSecuredSocket(this.ip.getHostAddress(), this.port, this.getAnt().proxied);
    this.s.getOutputStream().write(2);
    this.s.getOutputStream().flush();
    this.s.setSoTimeout(3 * 60000);
  }

  public SenderThread route(Message m){
    SenderThread st = new SenderThread(m, this);
    st.start();
    return st;
  }

  public void send(Message m) throws Exception{
    ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
    ZOutputStream zOut = new ZOutputStream(byteArrayOut, JZlib.Z_BEST_COMPRESSION);
    BandwidthFilterOut bfOut = new BandwidthFilterOut(zOut);
    ObjectOutputStream zOOs = new ObjectOutputStream(bfOut);
    _logger.debug(this.getIdent() + ": DCMessage sending ["+this.getQueuedMessages()+"]"+m.getType()+" "+m);//+" From: "+m.getSource()+" To: "+m.getDest());
    zOOs.writeObject(local.getLocalInetAddress());
    zOOs.writeObject(m);

    NeighbourAnt.totalUncompressionSizeOut += zOut.getTotalIn();

    zOut.close();

    NeighbourAnt.totalCompressedSizeOut += byteArrayOut.toByteArray().length;
    NeighbourAnt.updateOutStats(m, byteArrayOut.toByteArray().length);

    if(m instanceof QueryMessage){
      QueryBandwidthFilterOut.getInstance().writeQuery(byteArrayOut.toByteArray().length);
    }else{
      DataBandwidthFilterOut.getInstance().writeData(byteArrayOut.toByteArray().length);
    }
    s.getOutputStream().write(byteArrayOut.toByteArray());
    s.close();
    _logger.debug(this.getIdent() + ": DCMessage sent");
  }

  public String getIdent(){
    String curIp = ip.getHostAddress()+":"+port;
    if(curIp.startsWith("/"))
      curIp = curIp.substring(1);
    return curIp;
  }

  public Ant getAnt(){
    return this.local;
  }

  private synchronized void modifyQueuedMessages(int modifier){
    queuedMessages += modifier;
  }

  public void incQueuedMessages(){
    this.modifyQueuedMessages(1);
  }

  public void decQueuedMessages(){
    this.modifyQueuedMessages(-1);
  }

  public long getQueuedMessages(){
    return this.queuedMessages;
  }
}
